package vn.vfriends.vbox.bean;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.PostConstruct;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.shiro.SecurityUtils;
import org.primefaces.event.NodeCollapseEvent;
import org.primefaces.event.NodeExpandEvent;
import org.primefaces.model.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vn.vfriends.vbox.component.LazyFileTreeNode;
import vn.vfriends.vbox.model.Storage;
import vn.vfriends.vbox.model.File;
import vn.vfriends.vbox.model.Account;
import vn.vfriends.vbox.model.User;
import vn.vfriends.vbox.service.StorageService;
import vn.vfriends.vbox.service.AccountService;

/**
 *
 * @author tuancn
 */
@Named
@ViewScoped
public class HomeBean implements Serializable {
    
    private static final Logger logger = LoggerFactory.getLogger(HomeBean.class);
    
    @Inject
    private AccountService accountService;
    
    @Inject
    private StorageService storageService;
    
    private Account leftAccount;
    
    private Account rightAccount;
    
    private Storage leftStorage;
    
    private Storage rightStorage;
    
    private TreeNode leftRoot;
    
    private TreeNode rightRoot;
  
    private TreeNode leftSelectedNode;
    
    private TreeNode rightSelectedNode;
    
    private Set<TreeNode> expandingNodes = new HashSet<>();
    
    private List<Account> accountList;
    
    private String changedLeftAccountName;
    
    private String changedRightAccountName;
    
    private User loggedUser;

    @PostConstruct
    private void init() {
        loggedUser = (User)SecurityUtils.getSubject().getSession().getAttribute("LOGGED_USER");
        accountList = accountService.findByUserId(loggedUser.getId());
        
        if(accountList.isEmpty()) {
            try {
                FacesContext.getCurrentInstance().getExternalContext().redirect("storage/register.jsf");
                return;
            } catch (IOException ex) {
                logger.error(ex.getMessage(), ex);
            }
        }
        
        //  Initalize left panel's data
        leftAccount = accountList.get(0);
        changedLeftAccountName = leftAccount.getName();
        Future<List<File>> leftFilesFuture = storageService.getFiles(leftAccount.getId(), "/");
        Future<Storage> leftStorageFuture = storageService.getStorage(leftAccount.getId());
        
        //  Initalize right panel's data
        if(accountList.size() > 1) {
            rightAccount = accountList.get(1);
            changedRightAccountName = rightAccount.getName();
            Future<List<File>> rightFilesFuture = storageService.getFiles(rightAccount.getId(), "/");
            Future<Storage> rightStorageFuture = storageService.getStorage(rightAccount.getId());

            leftStorage = this.initStorage(leftStorageFuture);
            leftRoot = this.initRoot(leftFilesFuture, leftAccount.getId());

            rightStorage = this.initStorage(rightStorageFuture);
            rightRoot = this.initRoot(rightFilesFuture, rightAccount.getId());
        } else {
            rightAccount = leftAccount;
            changedRightAccountName = rightAccount.getName();
            leftStorage = this.initStorage(leftStorageFuture);
            leftRoot = this.initRoot(leftFilesFuture, leftAccount.getId());
            
            rightStorage = this.initStorage(leftStorageFuture);
            rightRoot = this.initRoot(leftFilesFuture, rightAccount.getId());
        }
        
    }

    private Storage initStorage(Future<Storage> storageFuture) {
        try {
            return storageFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            logger.error(e.getMessage(), e);
            return null;
        }
    }
    
    public TreeNode initRoot(Future<List<File>> filesFuture, Integer accountId) {
        TreeNode root = new LazyFileTreeNode("root", null, accountId);
        try {
            List<File> files = filesFuture.get();
            for (File file : files) {
                TreeNode node = new LazyFileTreeNode(file, root, accountId);
                
                if(file.isFolder()) {   // temp  child
                    new LazyFileTreeNode(null, node, null);
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            logger.error(e.getMessage(), e);
        }
        return root;
    }
    
    public String getChangedLeftAccountName() {
        return changedLeftAccountName;
    }

    public void setChangedLeftAccountName(String changedLeftAccountName) {
        this.changedLeftAccountName = changedLeftAccountName;
    }

    public String getChangedRightAccountName() {
        return changedRightAccountName;
    }

    public void setChangedRightAccountName(String changedRightAccountName) {
        this.changedRightAccountName = changedRightAccountName;
    }

    
    public List<Account> getAccountList() {
        return accountList;
    }

    public Account getLeftAccount() {
        return leftAccount;
    }

    public void setLeftAccount(Account leftAccount) {
        this.leftAccount = leftAccount;
    }

    public Account getRightAccount() {
        return rightAccount;
    }

    public void setRightAccount(Account rightAccount) {
        this.rightAccount = rightAccount;
    }

    public Storage getLeftStorage() {
        return leftStorage;
    }

    public Storage getRightStorage() {
        return rightStorage;
    }

    public TreeNode getLeftRoot() {
        return leftRoot;
    }

    public TreeNode getRightRoot() {
        return rightRoot;
    }

    public TreeNode getLeftSelectedNode() {
        return leftSelectedNode;
    }

    public void setLeftSelectedNode(TreeNode leftSelectedNode) {
        this.leftSelectedNode = leftSelectedNode;
    }

    public TreeNode getRightSelectedNode() {
        return rightSelectedNode;
    }

    public void setRightSelectedNode(TreeNode rightSelectedNode) {
        this.rightSelectedNode = rightSelectedNode;
    }
    
    public void onNodeExpand(NodeExpandEvent event) throws Exception {  
        LazyFileTreeNode treeNode = (LazyFileTreeNode)event.getTreeNode();
        
        this.expandingNodes.add(treeNode);
        
        File parent = (File)treeNode.getData();
        treeNode.getChildren().clear();
        
        Future<List<File>> filesFuture = storageService.getFiles(treeNode.getAccountId(), parent.getFileId());
        List<File> files;
        try {
            files = filesFuture.get();
        } catch (InterruptedException | ExecutionException ex) {
            logger.error(ex.getMessage());
            throw ex;
        }
        
        for(File file : files) {
            TreeNode node = new LazyFileTreeNode(file, treeNode, treeNode.getAccountId());
            if (file.isFolder()) {   // temp  child
                new LazyFileTreeNode(null, node, null);
            }
        }
    }
    
    public void onNodeCollapse(NodeCollapseEvent event) throws Exception {  
        LazyFileTreeNode treeNode = (LazyFileTreeNode)event.getTreeNode();
        this.expandingNodes.remove(treeNode);
    }
    
    public void reloadPanelAction(String position) {
        if(position.equals("left")) {
            Future<List<File>> filesFuture = storageService.getFiles(leftAccount.getId(), "/");
            Future<Storage> storageFuture = storageService.getStorage(leftAccount.getId());
            this.leftRoot = this.initRoot(filesFuture, leftAccount.getId());
            this.leftStorage = this.initStorage(storageFuture);
        } else {
            Future<List<File>> filesFuture = storageService.getFiles(rightAccount.getId(), "/");
            Future<Storage> storageFuture = storageService.getStorage(rightAccount.getId());
            this.rightRoot = initRoot(filesFuture, rightAccount.getId());
            this.rightStorage = initStorage(storageFuture);
        }
    }
    
    public void autoUpdateAction(String position) {
        try {
            if (position.equals("left")) {
                Future<Storage> storageFuture = storageService.getStorage(leftAccount.getId());
                this.leftStorage = storageFuture.get();
                
                this.reloadNode((LazyFileTreeNode) this.leftRoot);
            } else {
                Future<Storage> storageFuture = storageService.getStorage(rightAccount.getId());
                this.rightStorage = storageFuture.get();
                
                this.reloadNode((LazyFileTreeNode) this.rightRoot);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
    
    private void reloadNode(LazyFileTreeNode node) throws Exception {
        Future<List<File>> filesFuture;
        if(node.getData() instanceof File) {
            File parentFile = (File)node.getData();
            logger.debug("Reload node: {}", parentFile.getFileId());
            
            filesFuture = storageService.getFiles(node.getAccountId(), parentFile.getFileId());
        } else {
            logger.debug("Reload node: /");
            filesFuture = storageService.getFiles(node.getAccountId(), "/");
        }
        
        node.getChildren().clear();
        
        List<File> files = filesFuture.get();
        for(File file : files) {
            LazyFileTreeNode childNode = new LazyFileTreeNode(file, node, node.getAccountId());
            if(this.expandingNodes.contains(childNode)) {
                childNode.setExpanded(true);
                reloadNode(childNode);
            } else if (file.isFolder()) {   // temp  child
                new LazyFileTreeNode(null, childNode, null);
            }
        }
    }
    
    public void handleLeftAccountChange(AjaxBehaviorEvent event) {
        String changedAccountName = (String)((UIOutput)event.getSource()).getValue();
        
        leftAccount = this.accountService.findByName(this.loggedUser.getId(), changedAccountName);
        
        Future<List<File>> filesFuture = storageService.getFiles(leftAccount.getId(), "/");
        Future<Storage> storageFuture = storageService.getStorage(leftAccount.getId());

        leftStorage = this.initStorage(storageFuture);
        
        leftRoot.getChildren().clear();
        leftRoot = this.initRoot(filesFuture, leftAccount.getId());
    }
    
    public void handleRightAccountChange(AjaxBehaviorEvent event) {
        String changedAccountName = (String)((UIOutput)event.getSource()).getValue();
        
        rightAccount = this.accountService.findByName(this.loggedUser.getId(), changedAccountName);
        
        Future<List<File>> filesFuture = storageService.getFiles(rightAccount.getId(), "/");
        Future<Storage> storageFuture = storageService.getStorage(rightAccount.getId());

        rightStorage = this.initStorage(storageFuture);
        
        rightRoot.getChildren().clear();
        rightRoot = this.initRoot(filesFuture, rightAccount.getId());
    }
}
